/*
 * Copyright 2018-present KunMinX
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.pbase.ui.page;

import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.util.SparseArray;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.inputmethod.InputMethodManager;

import com.blankj.utilcode.util.Utils;
import com.hjq.toast.ToastUtils;
import com.hjq.xtoast.XToast;
import com.lxj.xpopup.XPopup;
import com.lxj.xpopup.impl.LoadingPopupView;
import com.pbase.BaseApplication;
import com.pbase.R;
import com.pbase.domain.usercase.BaseUserCase;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import androidx.databinding.ViewDataBinding;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;


/**
 * 参考 https://github.com/KunMinX/Strict-DataBinding
 * Create by KunMinX at 19/7/11
 */
public abstract class BaseFragment extends Fragment {

    protected AppCompatActivity mActivity;
    private ViewDataBinding mBinding;

    private ViewModelProvider mFragmentProvider;
    private ViewModelProvider mCustomLifeFragmentProvider;
    private ViewModelProvider mActivityProvider;
    private ViewModelProvider mApplicationProvider;



    private static final Handler HANDLER = new Handler();
    protected boolean mAnimationLoaded;

    private LoadingPopupView loadingPopup;

    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
        mActivity = (AppCompatActivity) context;
    }


    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        initViewModel();
    }


    protected abstract void onCreateViewBefore();

    protected abstract void onCreateViewAfter();
    @Nullable
    @Override
    public View onCreateView(
            @NonNull LayoutInflater inflater,
            @Nullable ViewGroup container,
            @Nullable Bundle savedInstanceState
    ) {
        onCreateViewBefore();
        AppManager.getInstance().addFragment(this);
        DataBindingConfig dataBindingConfig = getDataBindingConfig();

        //TODO tip: DataBinding 严格模式：
        // 将 DataBinding 实例限制于 base 页面中，默认不向子类暴露，
        // 通过这样的方式，来彻底解决 视图调用的一致性问题，
        // 如此，视图调用的安全性将和基于函数式编程思想的 Jetpack Compose 持平。

        // 如果这样说还不理解的话，详见 https://xiaozhuanlan.com/topic/9816742350 和 https://xiaozhuanlan.com/topic/2356748910

        ViewDataBinding binding = DataBindingUtil.inflate(inflater, dataBindingConfig.getLayout(), container, false);
        binding.setLifecycleOwner(this);
        binding.setVariable(dataBindingConfig.getVmVariableId(), dataBindingConfig.getStateViewModel());
        SparseArray bindingParams = dataBindingConfig.getBindingParams();
        for (int i = 0, length = bindingParams.size(); i < length; i++) {
            binding.setVariable(bindingParams.keyAt(i), bindingParams.valueAt(i));
        }
        mBinding = binding;
        //usercase 与界面生命周期绑定
        for (BaseUserCase baseUserCase : dataBindingConfig.getStateViewModel().getUserCaseList()) {
            baseUserCase.setTag(this.getClass());
            getLifecycle().addObserver(baseUserCase);
        }
        //土司提示
        dataBindingConfig.getStateViewModel().errorTip.observe(this, s -> {
            showToast(s);
        });
        //开关 等待提示
        dataBindingConfig.getStateViewModel().isShowWaitDialog.observe(getViewLifecycleOwner(), aBoolean -> {
            if (aBoolean) {
                showWaitDialog();
            } else {
                cancleWaitDialog();
            }
        });
        onCreateViewAfter();
        return binding.getRoot();
    }
    @Override
    public void onDestroyView() {
        super.onDestroyView();
        AppManager.getInstance().removeFragment(this);
        mBinding.unbind();
        mBinding = null;
    }
    /**
     * 只作ViewModel实例化
     */
    protected abstract void initViewModel();
    /**
     * 只作 DataBinding获取
     */
    protected abstract DataBindingConfig getDataBindingConfig();


    //TODO tip 1: DataBinding 严格模式（详见 DataBindingFragment - - - - - ）：
    // 将 DataBinding 实例限制于 base 页面中，默认不向子类暴露，
    // 通过这样的方式，来彻底解决 视图调用的一致性问题，
    // 如此，视图调用的安全性将和基于函数式编程思想的 Jetpack Compose 持平。
    // 如果这样说还不理解的话，详见 https://xiaozhuanlan.com/topic/9816742350 和 https://xiaozhuanlan
    // .com/topic/2356748910
    protected ViewDataBinding cautiousGetBinding() {
        return mBinding;
    }

    //TODO tip 2: Jetpack 通过 "工厂模式" 来实现 ViewModel 的作用域可控，
    //目前我们在项目中提供了 Application、Activity、Fragment 三个级别的作用域，
    //值得注意的是，通过不同作用域的 Provider 获得的 ViewModel 实例不是同一个，
    //所以如果 ViewModel 对状态信息的保留不符合预期，可以从这个角度出发去排查 是否眼前的 ViewModel 实例不是目标实例所致。

    //如果这样说还不理解的话，详见 https://xiaozhuanlan.com/topic/6257931840

    protected <T extends ViewModel> T getFragmentScopeViewModel(@NonNull Class<T> modelClass) {
        if (mFragmentProvider == null) {
            mFragmentProvider = new ViewModelProvider(this);
        }
        return mFragmentProvider.get(modelClass);
    }
    /**
     *    以指定fragment生命周期为依托存在的viewmodel
     *    适用于viewpage等fragment不被销毁的场景
     */

    protected <T extends ViewModel > T getCustomLifeFragmentScopeViewModel(@NonNull Class<T> modelClass,@NonNull Fragment fragemntClass) {
        if (mCustomLifeFragmentProvider == null) {
            mCustomLifeFragmentProvider = new ViewModelProvider(fragemntClass);
        }
        return mCustomLifeFragmentProvider.get(modelClass);
    }
    /**
     * 注 ：当Shared-viewmodel 使用mCustomLifeFragmentProvider 依托特定fragment 存在 来共享数据
     * ，将在该fragment退出后自行回数据，下次进入后不会有历史数据留存
     * 当 Shared-viewmodel 使用 mActivityProvider 依托activity 共享数据，需要手动处理 Shared-viewmodel历史数据
     * 否则容易出现历史数据影响新界面的情况
     */

    protected <T extends ViewModel> T getActivityScopeViewModel(@NonNull Class<T> modelClass) {
        if (mActivityProvider == null) {
            mActivityProvider = new ViewModelProvider(mActivity);
        }
        return mActivityProvider.get(modelClass);
    }

    protected <T extends ViewModel> T getApplicationScopeViewModel(@NonNull Class<T> modelClass) {
        if (mApplicationProvider == null) {
            mApplicationProvider = new ViewModelProvider(
                    (BaseApplication) mActivity.getApplicationContext() );
        }
        return mApplicationProvider.get(modelClass);
    }



    private Application checkApplication(Activity activity) {
        Application application = activity.getApplication();
        if (application == null) {
            throw new IllegalStateException("Your activity/fragment is not yet attached to "
                    + "Application. You can't request ViewModel before onCreate call.");
        }
        return application;
    }

    private void checkActivity(Fragment fragment) {
        Activity activity = fragment.getActivity();
        if (activity == null) {
            throw new IllegalStateException("Can't create ViewModelProvider for detached fragment");
        }
    }


    @Nullable
    @Override
    public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
        //TODO 错开动画转场与 UI 刷新的时机，避免掉帧卡顿的现象
        HANDLER.postDelayed(() -> {
            if (!mAnimationLoaded) {
                mAnimationLoaded = true;
                loadInitData();
            }
        }, 280);
        return super.onCreateAnimation(transit, enter, nextAnim);
    }

    protected void loadInitData() {

    }

    protected void toggleSoftInput() {
        InputMethodManager imm =
                (InputMethodManager) mActivity.getSystemService(Activity.INPUT_METHOD_SERVICE);
        imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS);
    }

    //根据用户的数据类型打开相应的Activity
    protected void openUrlInBrowser(String url) {
        Uri uri = Uri.parse(url);
        Intent intent = new Intent(Intent.ACTION_VIEW, uri);
        startActivity(intent);
    }
    /**
     * 显示等待框
     */
    protected void showWaitDialog() {
        showWaitDialog(getResources().getString(R.string.please_wait),0,null);
    }
    protected void showWaitDialog(String tip) {
        showWaitDialog(tip,0,null);
    }
    /**
     * 显示等待框
     */
    protected void showWaitDialog(String tip,long time,Runnable runnable) {
        if (loadingPopup == null) {
            loadingPopup = (LoadingPopupView) new XPopup.Builder(this.getContext())
                    .dismissOnBackPressed(false)
                    .dismissOnTouchOutside(false)
                    .isLightNavigationBar(true)
                    .asLoading(getResources().getString(R.string.please_wait))
                    .show() ;
            if (time !=0){
                if (runnable !=null){
                    loadingPopup. delayDismissWith (time ,runnable );
                }else {
                    loadingPopup. delayDismiss (time  );
                }

            }
        }else {
            loadingPopup.setTitle(tip);
            loadingPopup.show();
        }
    }


    /**
     * 取消等待框
     */
    protected void cancleWaitDialog() {
        if (loadingPopup!=null){
            loadingPopup.dismiss();
        }
    }
    public void showToast(String s ){
        showToast(s,2000);
    }
    /**
     * @param s
     * @param time 单位毫秒
     */
    public void showToast(String s,int time){
        if (((BaseActivity)this.getActivity()).isWindow ){
            new XToast<>(  Utils.getApp())
                    .setContentView(R.layout.toast_hint)
                    .setDuration(time)
                    .setAnimStyle(android.R.style.Animation_Translucent)
                    .setText(R.id.toast_tv, s)
                    .setGravity(Gravity.BOTTOM)
                    .setYOffset((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, getResources().getDisplayMetrics()))
                    .show();

        }else{
            ToastUtils.show(s);
        }

    }

}
